package pacman.entries.pacman;

import java.util.ArrayList;
import java.util.Collection;

import pacman.controllers.Controller;
import pacman.game.Constants.MOVE;
import pacman.game.Game;
import edu.ucsc.gameAI.Debug;
import edu.ucsc.gameAI.EatPills;
import edu.ucsc.gameAI.IAction;
import edu.ucsc.gameAI.MoveTowardsClosestEdibleGhostAction;
import edu.ucsc.gameAI.MoveTowardsClosestPowerPillAction;
import edu.ucsc.gameAI.RunAwayFromGhosts;
import edu.ucsc.gameAI.conditions.AreAnyGhostsEdibleAndNoDangerousGhostNearby;
import edu.ucsc.gameAI.conditions.AreAnyGhostsEdibleNearbyAndNoDangerousGhostNearby;
import edu.ucsc.gameAI.conditions.AreTherePowerPillsLeft;
import edu.ucsc.gameAI.conditions.DangerousGhostNearby;
import edu.ucsc.gameAI.conditions.NoDangerousGhostsNearby;
import edu.ucsc.gameAI.conditions.NoEdibleGhostsRemaining;
import edu.ucsc.gameAI.conditions.NoSafePathToAnyEdibleGhosts;
import edu.ucsc.gameAI.conditions.CheckHowManyPPRemainANDGhostDistance;
import edu.ucsc.gameAI.fsm.ITransition;
import edu.ucsc.gameAI.fsm.State;
import edu.ucsc.gameAI.fsm.StateMachine;
import edu.ucsc.gameAI.fsm.Transition;

/*
 * This is the class you need to modify for your entry. In particular, you need to
 * fill in the getAction() method. Any additional classes you write should either
 * be placed in this package or sub-packages (e.g., game.entries.pacman.mypackage).
 */
public class MyPacMan extends Controller<MOVE> {

	// Decision Tree implementation
	// private MOVE myMove = MOVE.NEUTRAL;
	// private BinaryDecision isGhostNearby, isPPNearby, isPelletNearby,
	// isSafePathToNearestPP, isSafePathToNearestPellet;
	// private IBinaryNode runAwayFromGhosts, moveTowardCloesestPellet,
	// moveTowardPP, moveTowardNotClosestPellet;

	// FSM Implementation
	private State eatPillsState;
	private State headTowardPPState;
	private State eatGhostsState;
	private State avoidDanger;
	private StateMachine fsm;

	public MyPacMan() {

		// BST
		// isGhostNearby = new BinaryDecision();
		// moveTowardCloesestPellet = new EatPills();
		// runAwayFromGhosts = new RunAwayFromGhosts();
		//
		// isGhostNearby.setCondition(new DangerousGhostNearby());
		// isGhostNearby.setTrueBranch(runAwayFromGhosts);
		// isGhostNearby.setFalseBranch(moveTowardCloesestPellet);

		// FSM
		fsm = new StateMachine();

		eatPillsState = new State("eatPillsState");
		headTowardPPState = new State("headTowardPPState");
		eatGhostsState = new State("eatGhostsState");
		avoidDanger = new State("avoidDanger");
		
		Collection<ITransition> eatPillsStateTrans = new ArrayList<ITransition>();
		Transition eatPillsTran1 = new Transition("eatPills->avoidDanger (Cond: DangerGhostsNearby)");
		eatPillsTran1.setCondition(new DangerousGhostNearby());
		eatPillsTran1.setTargetState(avoidDanger);
		eatPillsTran1.setAction(new RunAwayFromGhosts());
		eatPillsStateTrans.add(eatPillsTran1);	
		//TECHNICALLY this shouldn't happen, but on the off chance that pacman accidentally eats a power pill
		//we he wasn't supposed to, we still want him to take advantage of his new found powers and chase after 
		//the weak ghosts.
		Transition eatPillsTran2 = new Transition("eatPills->killNearbyGhosts (Cond: EdibleGhostsExistNearbyANDNoDangerousGhostNearby)");
		eatPillsTran2.setCondition(new AreAnyGhostsEdibleNearbyAndNoDangerousGhostNearby());
		eatPillsTran2.setTargetState(eatGhostsState);
		eatPillsTran2.setAction(new MoveTowardsClosestEdibleGhostAction());
		eatPillsStateTrans.add(eatPillsTran2);
		//add to state:
		eatPillsState.setTransitions(eatPillsStateTrans);
		eatPillsState.setAction(new EatPills());

		
		//headTowardsPPState (transition definitions)
		Collection<ITransition> headTowardPPStateTrans = new ArrayList<ITransition>();		

//		Transition headTowardPPTran = new Transition("headTowardsPP->avoidDanger (Cond: DangerousGhostsNearby)");
//		headTowardPPTran.setCondition(new DangerousGhostNearby());
//		headTowardPPTran.setTargetState(avoidDanger);
//		headTowardPPTran.setAction(new RunAwayFromGhosts());
//		headTowardPPStateTrans.add(headTowardPPTran);
		Transition headTowardPPTran2 = new Transition("headTowardsPP->eatGhosts (Cond: PowerPillsRemain");
		headTowardPPTran2.setCondition(new CheckHowManyPPRemainANDGhostDistance());
		headTowardPPTran2.setTargetState(eatGhostsState);
		headTowardPPTran2.setAction(new MoveTowardsClosestEdibleGhostAction());
		headTowardPPStateTrans.add(headTowardPPTran2);
		Transition headTowardPPTran1 = new Transition("headTowardsPP->eatPills (Cond: NoDangerousGhostsNearby)");
		headTowardPPTran1.setCondition(new NoDangerousGhostsNearby());
		headTowardPPTran1.setTargetState(eatPillsState);
		headTowardPPTran1.setAction(new EatPills());
		headTowardPPStateTrans.add(headTowardPPTran1);
		//add to state:
		headTowardPPState.setTransitions(headTowardPPStateTrans);
		headTowardPPState.setAction(new MoveTowardsClosestPowerPillAction());
		
		
		
		//EatGhostsState (transition definitions)
		Collection<ITransition> eatGhostsStateTransitions = new ArrayList<ITransition>();
//		Transition eatGhostsTran1 = new Transition("EatGhostsState->avoidDanger (Cond DangerousGhostsNearby)");
//		eatGhostsTran1.setCondition(new DangerousGhostNearby());
//		eatGhostsTran1.setTargetState(avoidDanger);
//		eatGhostsTran1.setAction(new RunAwayFromGhosts());
//		eatGhostsStateTransitions.add(eatGhostsTran1);
		Transition eatGhostsTran2 = new Transition("EatGhostsState->EatRegularPills (Cond NoEdibleGhostsExist)");
		eatGhostsTran2.setCondition(new NoEdibleGhostsRemaining());
		eatGhostsTran2.setTargetState(eatPillsState);
		eatGhostsTran2.setAction(new EatPills());
		eatGhostsStateTransitions.add(eatGhostsTran2);
		Transition eatGhostsTran3 = new Transition("EatGhostsState->EatRegularPills (Cond NoSafePathToAnyEdibleGhosts)");
		eatGhostsTran3.setCondition(new NoSafePathToAnyEdibleGhosts());
		eatGhostsTran3.setTargetState(eatPillsState);
		eatGhostsTran3.setAction(new EatPills());
		eatGhostsStateTransitions.add(eatGhostsTran3);
		//add to state:		
		eatGhostsState.setTransitions(eatGhostsStateTransitions);
		eatGhostsState.setAction(new MoveTowardsClosestEdibleGhostAction());
		
		
		//AvoidDanger! (transition definitions)
		Collection<ITransition> avoidDangerTransisitions = new ArrayList<ITransition>();
		Transition avoidDangerTran1 = new Transition("avoidDanger->headTowardsPP (Cond: PowerPillsExist)");
		avoidDangerTran1.setCondition(new AreTherePowerPillsLeft());
		avoidDangerTran1.setTargetState(headTowardPPState);
		avoidDangerTran1.setAction(new MoveTowardsClosestPowerPillAction());
		avoidDangerTransisitions.add(avoidDangerTran1);
		Transition avoidDangerTran2 = new Transition("avoidDanger->eatGhosts (Cond: EdibleGhostsExistANDNoDangerousGhostNearby)");
		avoidDangerTran2.setCondition(new AreAnyGhostsEdibleAndNoDangerousGhostNearby());
		avoidDangerTran2.setTargetState(eatGhostsState);
		avoidDangerTran2.setAction(new MoveTowardsClosestEdibleGhostAction());
		avoidDangerTransisitions.add(avoidDangerTran2);
		Transition avoidDangerTran3 = new Transition("avoidDanger->EatRegularPills (Cond NoDangerousGhostNearby)");
		avoidDangerTran3.setCondition(new NoDangerousGhostsNearby());
		avoidDangerTran3.setTargetState(eatPillsState);
		avoidDangerTran3.setAction(new EatPills());
		avoidDangerTransisitions.add(avoidDangerTran3);
		//add to state
		avoidDanger.setTransitions(avoidDangerTransisitions);
		avoidDanger.setAction(new RunAwayFromGhosts());

		
		fsm.setCurrentState(eatPillsState); 
		
	}


	public MOVE getMove(Game game, long timeDue) {

		//GameView.addPoints(game, Color.DARK_GRAY, game.getJunctionIndices());
		
		if(game.wasPacManEaten()){
			Debug.debug("OOOF! PACMAN GOT EATEN! Live Remaining: " + game.getPacmanNumberOfLivesRemaining(), Debug.MESSAGETYPE.STATEMENT);
		}
		
		// FSM
		Collection<IAction> actions = fsm.update(game);
		//System.out.println("action list has:" + actions.size());

		

		
		for (IAction action : actions) {
			//Debug.debug(action.toString(), Debug.MESSAGETYPE.STATEMENT);
			if (action.getClass() == RunAwayFromGhosts.class)
				return new RunAwayFromGhosts().getMove(game, fsm.getCurrentState());
			else if (action.getClass() == EatPills.class)
				return new EatPills().getMove(game, fsm.getCurrentState());
			else if (action.getClass() == MoveTowardsClosestPowerPillAction.class)
				return new MoveTowardsClosestPowerPillAction().getMove(game, fsm.getCurrentState());
			else if (action.getClass() == MoveTowardsClosestEdibleGhostAction.class)
				return new MoveTowardsClosestEdibleGhostAction().getMove(game, fsm.getCurrentState());
			else {
				Debug.debug("No recognized actions returned! Returning a default value for getMove", Debug.MESSAGETYPE.STATEMENT);
				return action.getMove();
			}
		}
		Debug.debug("We didn't even make it into the forloop for some reason.", Debug.MESSAGETYPE.STATEMENT);
		return null;

		// BST
		// IAction action = isGhostNearby.makeDecision(game);
		// return action.getMove();
		// return isGhostNearby.makeDecision(game).getMove();
	}
}